package handlers

import (
	"chinaport-proxy/protocol"
	"chinaport-proxy/utils"
	"context"
	"encoding/json"
	"fmt"
	"log/slog"
	"net/http"
	"time"

	"github.com/gin-gonic/gin"
	"github.com/google/uuid"
)

// ProxyHandler handles HTTP to WebSocket proxy requests
type ProxyHandler struct {
	ukeyClient     UKeyClient
	password       string
	webhookService WebhookService
}

// NewProxyHandler creates a new proxy handler
func NewProxyHandler(ukeyClient UKeyClient, password string) *ProxyHandler {
	return &ProxyHandler{
		ukeyClient: ukeyClient,
		password:   password,
	}
}

// SetWebhookService sets the webhook service for notifications
func (h *ProxyHandler) SetWebhookService(ws WebhookService) {
	h.webhookService = ws
}

// RPCRequest represents the incoming RPC request format
type RPCRequest struct {
	Method string          `json:"_method" binding:"required"`
	Args   json.RawMessage `json:"args"`
}

// HandleRPC handles RPC requests
func (h *ProxyHandler) HandleRPC(c *gin.Context) {
	// Track request start time for audit
	startTime := time.Now()

	// Generate request ID
	requestID := uuid.New().String()

	// Create logger with request ID
	logger := slog.With("request_id", requestID)

	// Add request ID to response header
	c.Header("X-Request-ID", requestID)

	var req RPCRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		logger.Warn("Invalid request format", "error", err)
		errorResp := gin.H{
			"error": "Invalid request format: " + err.Error(),
		}

		// Send audit webhook for failed request
		if h.webhookService != nil {
			duration := time.Since(startTime).Milliseconds()
			h.webhookService.SendAudit(context.Background(), requestID, "", nil, errorResp, false, err.Error(), duration)
		}

		c.JSON(http.StatusBadRequest, errorResp)
		return
	}

	logger.Info("Received RPC request", "method", req.Method)

	// Log request args in verbose mode (with password masked)
	if slog.Default().Enabled(nil, slog.LevelDebug) && len(req.Args) > 0 {
		logger.Debug("Request args", "args", utils.MaskPassword(string(req.Args)))
	}

	// Parse args
	var args interface{}
	if len(req.Args) > 0 && string(req.Args) != "null" {
		if err := json.Unmarshal(req.Args, &args); err != nil {
			logger.Warn("Failed to unmarshal args", "error", err)
			args = nil
		}
	} else {
		args = nil
	}

	// Call UKey with request context
	logger.Debug("Calling UKey", "method", req.Method)
	resp, err := h.ukeyClient.CallWithRequestID(protocol.Method(req.Method), args, requestID)
	if err != nil {
		logger.Warn("UKey communication error", "error", err, "method", req.Method)
		errorResp := gin.H{
			"error": "UKey communication error: " + err.Error(),
		}

		// Send audit webhook for failed communication
		if h.webhookService != nil {
			duration := time.Since(startTime).Milliseconds()
			h.webhookService.SendAudit(context.Background(), requestID, req.Method, args, errorResp, false, err.Error(), duration)
		}

		c.JSON(http.StatusServiceUnavailable, errorResp)
		return
	}

	// Log response in verbose mode
	if slog.Default().Enabled(nil, slog.LevelDebug) {
		respJSON, _ := json.Marshal(resp)
		logger.Debug("Response data", "response", utils.MaskPassword(string(respJSON)))
	}

	// Remove _id from response (client doesn't need internal request ID)
	cleanResp := gin.H{
		"_method": resp.Method,
		"_status": resp.Status,
		"_args":   resp.Args,
	}

	// Calculate request duration
	duration := time.Since(startTime).Milliseconds()

	// Check if the UKey operation was successful
	if !resp.Args.Result {
		logger.Warn("UKey operation failed", "method", req.Method, "errors", resp.Args.Error)

		// Send failure webhook notification if configured
		if h.webhookService != nil {
			go h.webhookService.NotifyOperationFailure(context.Background(), string(req.Method), resp.Args.Error)
			// Also send audit webhook
			h.webhookService.SendAudit(context.Background(), requestID, req.Method, args, cleanResp, false,
				fmt.Sprintf("UKey operation failed: %v", resp.Args.Error), duration)
		}

		// Return 400 Bad Request for UKey operation failures
		// (could be wrong method, wrong password, hardware issues, etc.)
		c.JSON(http.StatusBadRequest, cleanResp)
	} else {
		logger.Info("Request completed successfully", "method", req.Method)

		// Send audit webhook for successful request
		if h.webhookService != nil {
			h.webhookService.SendAudit(context.Background(), requestID, req.Method, args, cleanResp, true, "", duration)
		}

		c.JSON(http.StatusOK, cleanResp)
	}
}

func (h *ProxyHandler) sendAudit(ctx context.Context, requestID, action string, req, resp interface{}, success bool, errMsg string, start time.Time) {
	if h.webhookService == nil {
		return
	}
	h.webhookService.SendAudit(ctx, requestID, action, req, resp, success, errMsg, time.Since(start).Milliseconds())
}

func (h *ProxyHandler) startRequest(c *gin.Context) (string, *slog.Logger, time.Time) {
	requestID := uuid.New().String()
	logger := slog.With("request_id", requestID)
	c.Header("X-Request-ID", requestID)
	return requestID, logger, time.Now()
}
